package mqtt

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"log"
	"os"
	"time"

	mqtt "github.com/eclipse/paho.mqtt.golang"
)

// Client MQTT 客户端封装
type Client struct {
	client   mqtt.Client
	config   *Config
	isConnected bool
}

// NewClient 创建 MQTT 客户端
// token: ZKP 认证后获得的 JWT token，用作 MQTT 密码
func NewClient(config *Config, token string) (*Client, error) {
	if config.Broker == "" {
		return nil, fmt.Errorf("MQTT broker 地址不能为空")
	}

	// 设置 MQTT 客户端选项
	opts := mqtt.NewClientOptions()
	opts.AddBroker(config.Broker)
	opts.SetClientID(config.ClientID)

	// 使用 JWT token 进行认证
	if config.Username != "" {
		opts.SetUsername(config.Username)
	}
	if token != "" {
		opts.SetPassword(token) // ✅ 使用 JWT token 作为密码
	}

	// 连接参数
	opts.SetKeepAlive(60 * time.Second)
	opts.SetPingTimeout(10 * time.Second)
	opts.SetAutoReconnect(true)
	opts.SetMaxReconnectInterval(10 * time.Second)
	opts.SetConnectRetry(true)
	opts.SetCleanSession(false) // 🔧 修复:使用持久会话,避免重复ClientID冲突
	opts.SetConnectRetryInterval(2 * time.Second) // 重连间隔2秒
	opts.SetMaxReconnectInterval(30 * time.Second) // 最大重连间隔30秒

	// 遗嘱消息（设备异常断开时通知）
	willTopic := fmt.Sprintf("devices/%s/status", config.ClientID)
	willPayload := fmt.Sprintf(`{"device_id":"%s","status":"offline","timestamp":"%s"}`,
		config.ClientID, time.Now().Format(time.RFC3339))
	opts.SetWill(willTopic, willPayload, 1, false)

	// 配置TLS（如果启用或broker地址使用SSL/TLS协议）
	brokerUsesTLS := false
	if len(config.Broker) > 6 {
		protocol := config.Broker[:6]
		if protocol == "ssl://" || protocol == "tls://" || protocol == "tcps://" {
			brokerUsesTLS = true
		}
	}

	if config.TLS != nil && (config.TLS.Enabled || brokerUsesTLS) {
		tlsConfig := &tls.Config{
			InsecureSkipVerify: config.TLS.InsecureSkipVerify,
		}

		// 加载CA证书（如果提供）
		if config.TLS.CAFile != "" {
			caCertPEM, err := os.ReadFile(config.TLS.CAFile)
			if err != nil {
				return nil, fmt.Errorf("读取CA证书失败: %w", err)
			}

			// 创建证书池并添加CA证书
			certPool := x509.NewCertPool()
			if !certPool.AppendCertsFromPEM(caCertPEM) {
				return nil, fmt.Errorf("解析CA证书失败: 无法添加到证书池")
			}

			tlsConfig.RootCAs = certPool
			log.Printf("✅ 已加载CA证书: %s", config.TLS.CAFile)
		}

		// 加载客户端证书和私钥（如果提供）
		if config.TLS.CertFile != "" && config.TLS.KeyFile != "" {
			cert, err := tls.LoadX509KeyPair(config.TLS.CertFile, config.TLS.KeyFile)
			if err != nil {
				return nil, fmt.Errorf("加载客户端证书失败: %w", err)
			}
			tlsConfig.Certificates = []tls.Certificate{cert}
			log.Printf("✅ 已加载客户端证书: %s", config.TLS.CertFile)
		}

		opts.SetTLSConfig(tlsConfig)
		log.Printf("🔒 MQTT TLS已启用 (InsecureSkipVerify: %v)", config.TLS.InsecureSkipVerify)
	}

	c := &Client{
		config:      config,
		isConnected: false,
	}

	// 设置连接状态回调
	opts.SetConnectionLostHandler(c.onConnectionLost)
	opts.SetOnConnectHandler(c.onConnect)
	opts.SetReconnectingHandler(c.onReconnecting)

	// 创建客户端
	c.client = mqtt.NewClient(opts)

	// 连接到 broker
	log.Printf("正在连接到 MQTT broker: %s", config.Broker)
	if token := c.client.Connect(); token.Wait() && token.Error() != nil {
		return nil, fmt.Errorf("MQTT 连接失败: %v", token.Error())
	}

	c.isConnected = true
	log.Printf("✅ MQTT 连接成功 (Client ID: %s)", config.ClientID)

	return c, nil
}

// Publish 发布消息
func (c *Client) Publish(topic string, payload interface{}) error {
	if !c.isConnected {
		return fmt.Errorf("MQTT 未连接")
	}

	token := c.client.Publish(topic, c.config.QoS, c.config.Retained, payload)
	token.Wait()

	if err := token.Error(); err != nil {
		log.Printf("❌ 发布失败 [%s]: %v", topic, err)
		return err
	}

	return nil
}

// IsConnected 检查连接状态
func (c *Client) IsConnected() bool {
	return c.client.IsConnected()
}

// Disconnect 断开连接
func (c *Client) Disconnect() {
	log.Println("断开 MQTT 连接...")
	c.client.Disconnect(250)
	c.isConnected = false
	log.Println("✅ MQTT 已断开")
}

// onConnect 连接成功回调
func (c *Client) onConnect(client mqtt.Client) {
	c.isConnected = true
	log.Printf("✅ MQTT 已连接到 %s", c.config.Broker)

	// 🔧 修复:使用goroutine异步发布上线消息,避免阻塞回调
	go func() {
		statusTopic := fmt.Sprintf("devices/%s/status", c.config.ClientID)
		statusPayload := fmt.Sprintf(`{"device_id":"%s","status":"online","timestamp":"%s"}`,
			c.config.ClientID, time.Now().Format(time.RFC3339))

		if token := client.Publish(statusTopic, 1, false, statusPayload); token.Wait() && token.Error() != nil {
			log.Printf("⚠️  发布上线状态失败: %v", token.Error())
		}
	}()
}

// onConnectionLost 连接断开回调
func (c *Client) onConnectionLost(client mqtt.Client, err error) {
	c.isConnected = false
	log.Printf("⚠️  MQTT 连接断开: %v", err)
}

// onReconnecting 重连回调
func (c *Client) onReconnecting(client mqtt.Client, opts *mqtt.ClientOptions) {
	log.Println("🔄 正在重连 MQTT...")
}
